package zgame.bussiness;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import zgame.bean.ChatRoom;
import zgame.bean.Message;
import zgame.bean.User;
import zgame.main.Global;
import zgame.socket.DataPackage;
import zgame.socket.ProtocolConstants;
import zgame.socket.server.ServerConnection;

public class ChatBussiness {
  private static final Logger log = Logger.getLogger(ChatBussiness.class);

  public static void onCreateRoomRequest(ServerConnection server, DataPackage inputDataPackage) {
    // Receive data
    String roomName = inputDataPackage.nextString();
    String password = inputDataPackage.nextString();
    String roomTitle = inputDataPackage.nextString();
    int maxUser = inputDataPackage.nextInt();
    
    // Create new chat room object
    ChatRoom newChatRoom = new ChatRoom();
    newChatRoom.setName(roomName);
    newChatRoom.setRoomMaster(server.user);
    newChatRoom.setPasswordMd5(password);
    newChatRoom.setTitle(roomTitle);
    newChatRoom.setMaxUser(maxUser);
    log.info("User " + server.user.getUsername() + " try to create new room: " + newChatRoom);
    
    // Check if that room exist
    ChatRoom existRoom = Global.chatRooms.get(roomName);
    if (existRoom != null) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_CREATE_ROOM_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.DUPPLICATE_ROOM_ERROR);
      server.write(dataPackage);
      return;
    }
    
    if (maxUser < 0) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_CREATE_ROOM_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.MAX_USER_MUST_GREATER_THAN_ZERO);
      server.write(dataPackage);
      return;
    }
    
    // Store new room
    Global.chatRooms.put(roomName, newChatRoom);
    newChatRoom.addMember(server.user);
    log.info("User " + server.user.getUsername() + " created room " + roomName);
    
    // Send success result to user
    DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_CREATE_ROOM_SUCCESS_RESPONSE);
    dataPackage.putString(newChatRoom.getName());
    dataPackage.putString(newChatRoom.getTitle());
    dataPackage.putString(server.user.getUsername());
    dataPackage.putInt("".equals(newChatRoom.getPasswordMd5()) ? 0 : 1);
    dataPackage.putInt(newChatRoom.getNumberOfMember());
    dataPackage.putInt(newChatRoom.getMaxUser());
    server.write(dataPackage);
  }
  
  public static void onJoinChatRoomRequest(ServerConnection server, DataPackage inputDataPackage) {
    String roomName = inputDataPackage.nextString();
    String password = inputDataPackage.nextString();
    
    // Check if chat room exist
    ChatRoom chatRoom = Global.chatRooms.get(roomName);
    if (chatRoom == null) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_JOIN_CHAT_ROOM_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.ROOM_DOES_NOT_EXIST_ERROR);
      server.write(dataPackage);
      return;
    }
    
    // Check if chat room full
    if (chatRoom.getNumberOfMember() >= chatRoom.getMaxUser()) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_JOIN_CHAT_ROOM_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.CHAT_ROOM_FULL_ERROR);
      server.write(dataPackage);
      return;
    }
    
    // Check room password
    if (!password.equals(chatRoom.getPasswordMd5())) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_JOIN_CHAT_ROOM_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.CHAT_ROOM_PASSWORD_WRONG);
      server.write(dataPackage);
      return;
    }
    
    // Add member to chat room
    chatRoom.addMember(server.user);
    
    // Notify to client that join chat room success
    User roomMaster = chatRoom.getRoomMaster();
    DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_JOIN_CHAT_ROOM_SUCCESS_RESPONSE);
    dataPackage.putString(roomName);
    dataPackage.putString(chatRoom.getTitle());
    dataPackage.putString((roomMaster == null) ? "" : roomMaster.getUsername());
    dataPackage.putInt("".equals(chatRoom.getPasswordMd5()) ? 0 : 1);
    dataPackage.putInt(chatRoom.getNumberOfMember());
    dataPackage.putInt(chatRoom.getMaxUser());
    server.write(dataPackage);
    
    // Return the member list to client
    sendBackRoomMemberList(server, chatRoom);
  }
  
  public static void onSendMessageRequest(ServerConnection server, DataPackage inputDataPackage) {
    String destName = inputDataPackage.nextString();
    String messageText = inputDataPackage.nextString();
    
    // Send message to room
    ChatRoom chatRoom = Global.chatRooms.get(destName);
    if (chatRoom != null) {
      if (chatRoom.getMember(server.user.getUsername()) != null) {
        chatRoom.broadcastMessage(server.user.getUsername(), messageText);
      }
      return;
    }
    
    // Send message to user online
    ServerConnection toUserConn = Global.connectionMap.get(destName);
    if (toUserConn != null) {
      DataPackage messagePackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_MESSAGE_RECEIVE_RESPONSE);
      messagePackage.putString(server.user.getUsername());
      messagePackage.putString(toUserConn.user.getUsername());
      messagePackage.putString(messageText);
      toUserConn.write(messagePackage);
      return;
    }
    
    // Store message as offline message
    Map<String, Message> messages = Global.mesageOfflineStoreMap.get(destName);
    if (messages == null) {
      messages = new HashMap<String, Message>();
      Global.mesageOfflineStoreMap.put(destName, messages);
    }
    
    Message message = messages.get(server.user.getUsername());
    if (message == null) {
      message = new Message().setFromUser(server.user.getUsername()).setToUser(destName).setMessage(messageText);
      messages.put(server.user.getUsername(), message);
    } else {
      message.appendMessage(messageText);
    }
  }
  
  public static void onChangeRoomPasswordRequest(ServerConnection server, DataPackage inputDataPackage) {
  }
  
  public static void onKickMemberRequest(ServerConnection server, DataPackage inputDataPackage) {
  }
  
  public static void onInviteChatRequest(ServerConnection server, DataPackage inputDataPackage) {
  }
  
  public static void onBlockUserRequest(ServerConnection server, DataPackage inputDataPackage) {
  }
  
  public static void checkToSendMessageWhenUserComeToOnline(ServerConnection server) {
    Map<String, Message> messages = Global.mesageOfflineStoreMap.get(server.user.getUsername());
    if (messages == null) {
      messages = new HashMap<String, Message>();
      Global.mesageOfflineStoreMap.put(server.user.getUsername(), messages);
    }
    
    for (Message message : messages.values()) {
      DataPackage messagePackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_MESSAGE_RECEIVE_RESPONSE);
      messagePackage.putString(message.getFromUser());
      messagePackage.putString(server.user.getUsername());
      messagePackage.putString(message.getMessage());
      server.write(messagePackage);
      messages.remove(message);
    }
  }
  
  public static void onChatRoomListRequest(ServerConnection server, DataPackage inputDataPackage) {
    // TODO: Need split to page 
    DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_ROOM_LIST_RESPONSE);
    dataPackage.putInt(Global.chatRooms.size());
    for (ChatRoom chatRoom : Global.chatRooms.values()) {
      User roomMaster = chatRoom.getRoomMaster();
      
      dataPackage.putString(chatRoom.getName());
      dataPackage.putString(chatRoom.getTitle());
      dataPackage.putString((roomMaster == null) ? "" : roomMaster.getUsername());
      dataPackage.putInt("".equals(chatRoom.getPasswordMd5()) ? 0 : 1);
      dataPackage.putInt(chatRoom.getNumberOfMember());
      dataPackage.putInt(chatRoom.getMaxUser());
    }
    server.write(dataPackage);
  }
  
  public static void onUserLeaveRoomRequest(ServerConnection server, DataPackage inputDataPackage) {
    String roomName = inputDataPackage.nextString();
    
    ChatRoom chatRoom = Global.chatRooms.get(roomName);
    if (chatRoom != null) {
      boolean result = chatRoom.removeMember(server.user);
      if (result) {
        // If there's no user remain in room, then remove room
        if (chatRoom.getNumberOfMember() == 0) {
          Global.chatRooms.remove(roomName);
        }
        
        // Notify to client that leave room success
        DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_LEAVE_ROOM_SUCCESS_RESPONSE);
        server.write(dataPackage);
        return;
      }
    }
    
    DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_LEAVE_ROOM_FAIL_RESPONSE);
    server.write(dataPackage);
  }
  
  public static void onRoomMemberListRequest(ServerConnection server, DataPackage inputDataPackage) {
    String roomName = inputDataPackage.nextString();
    
    // Check if that room exist
    ChatRoom existRoom = Global.chatRooms.get(roomName);
    if (existRoom == null) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_GET_ROOM_MEMBER_LIST_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.ROOM_DOES_NOT_EXIST_ERROR);
      dataPackage.putString(roomName);
      server.write(dataPackage);
      return;
    }
    
    // Check if user is member of room or not
    if (existRoom.getMember(server.user.getUsername()) == null) {
      DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_GET_ROOM_MEMBER_LIST_FAIL_RESPONSE);
      dataPackage.putInt(ProtocolConstants.ErrorCode.CHAT_MUST_BE_ROOM_MEMBER_TO_DO_ACTION);
      dataPackage.putString(roomName);
      server.write(dataPackage);
      return;
    }
    
    // Return the member list to client
    sendBackRoomMemberList(server, existRoom);
  }
  
  public static void notifyUserGetInRoom(ChatRoom room, User user) {
    DataPackage userGetInNotifyPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_USER_GET_IN_NOTIFY_RESPONSE);
    userGetInNotifyPackage.putString(user.getUsername());
    userGetInNotifyPackage.putString(room.getName());
    
    for (User member : room.getMembers()) {
      DataPackage dataPackage = userGetInNotifyPackage.clone();
      member.server.write(dataPackage);
    }
  }
  
  public static void notifyUserGetOutRoom(ChatRoom room, User user) {
    DataPackage userGetOutNotifyPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_USER_GET_OUT_NOTIFY_RESPONSE);
    userGetOutNotifyPackage.putString(user.getUsername());
    userGetOutNotifyPackage.putString(room.getName());
    
    for (User member : room.getMembers()) {
      DataPackage dataPackage = userGetOutNotifyPackage.clone();
      member.server.write(dataPackage);
    }
  }
  
  public static void broadcastMessage(String fromUser, String message, ChatRoom room) {
    DataPackage messagePackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_MESSAGE_RECEIVE_RESPONSE);
    messagePackage.putString(fromUser);
    messagePackage.putString(room.getName()); // target is roomName
    messagePackage.putString(message);
    
    for (User user : room.getMembers()) {
      DataPackage dataPackage = messagePackage.clone();
      user.server.write(dataPackage);
    }
  }
  
  private static void sendBackRoomMemberList(ServerConnection server, ChatRoom room) {
    DataPackage dataPackage = new DataPackage(ProtocolConstants.ResponseHeader.CHAT_ROOM_MEMBER_LIST_RESPONSE);
    dataPackage.putString(room.getName());
    
    List<User> roomMembers = room.getMembers();
    dataPackage.putInt(roomMembers.size());
    for(User user : roomMembers) {
      dataPackage.putString(user.getUsername());
    }
    server.write(dataPackage);
  }
}
